package org.mozhu.mboot.core.web.filter;

import org.mozhu.mboot.core.web.HttpServletRequestSkipper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * <p>
 * 配置DeleteFilterClass
 * </p>
 * 1).可以由init-param指定delegateClass
 * <p>
 * <pre>
 * &lt;init-param&gt;
 * 	&lt;param-name&gt;delegateClass&lt;/param-name&gt;
 * 	&lt;param-value&gt;xxx.xxx.XXXDelegateFilter&lt;/param-value&gt;
 * &lt;/init-param&gt;
 * </pre>
 * <p>
 * 2).可以通过子类集成BaseFilter并覆盖getDelegateFilterClassName的方式来指定delegateClass
 *
 * @author ZhangPei
 */
public class DelegatingFilterProxy extends BaseFilter {

    private final Logger logger = LoggerFactory.getLogger(DelegatingFilterProxy.class);

    private Filter delegate;

    public DelegatingFilterProxy() {
    }

    public DelegatingFilterProxy(Filter delegate) {
        this.delegate = delegate;
    }

    public DelegatingFilterProxy(Filter delegate, HttpServletRequestSkipper skipper) {
        super(skipper);
        this.delegate = delegate;
    }

    @Override
    protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        String servletPath = request.getServletPath();
        logger.debug("Do Delegate Filter [{}], SevletPath [{}]", getDelegateFilterName(), servletPath);
        doDelegateFilter(request, response, filterChain);
    }

    @Override
    protected String getFilterName() {
        return getDelegateFilterName();
    }

    protected void doDelegateFilter(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        delegate.doFilter(request, response, filterChain);
    }

    @Override
    protected void init() throws ServletException {
        super.init();
        initDelegateFilter();
    }

    protected String getDelegateFilterName() {
        return getDelegateFilter().getClass().getName();
    }

    protected void initDelegateFilter() throws ServletException {
        if (this.delegate != null) {
            this.delegate.init(this.getFilterConfig());
            return;
        }
        Filter delegateFilter = createDelegateFilter();
        if (delegateFilter == null) {
            logger.error("Delegate Filter should not be null");
            throw new ServletException("Delegate Filter should not be null");
        }
        delegateFilter.init(getFilterConfig());
        this.delegate = delegateFilter;
    }

    protected Filter createDelegateFilter() throws ServletException {
        String delegateFilterClassName = this.getDelegateFilterClassName();
        if (delegateFilterClassName == null) {
            logger.error("Delegate Filter Class not defined");
            throw new ServletException("Delegate Filter Class not defined");
        }
        try {
            Class<?> delegateFilterClass = Class.forName(delegateFilterClassName);
            if (!Filter.class.isAssignableFrom(delegateFilterClass)) {
                logger.error("Delegate Filter Class [" + delegateFilterClass + "] is not subclass of [" + Filter.class + "]");
                throw new ServletException("Delegate Filter Class [" + delegateFilterClass + "] is not subclass of [" + Filter.class + "]");
            }
            return (Filter) delegateFilterClass.newInstance();
        } catch (ClassNotFoundException e) {
            logger.error("Delegate Filter Class [" + delegateFilterClassName + "] not found");
            throw new ServletException("Delegate Filter Class [" + delegateFilterClassName + "] not found", e);
        } catch (InstantiationException e) {
            logger.error("Fail to create instance for Delegate Filter Class [" + delegateFilterClassName + "]");
            throw new ServletException("Fail to create instance for Delegate Filter Class [" + delegateFilterClassName + "]", e);
        } catch (IllegalAccessException e) {
            logger.error("Fail to create instance for Delegate Filter Class [" + delegateFilterClassName + "]");
            throw new ServletException("Fail to create instance for Delegate Filter Class [" + delegateFilterClassName + "]", e);
        }
    }

    protected String getDelegateFilterClassName() {
        String delegateClassName = getFilterConfig().getInitParameter(PAREMETER_KEY_DELEGATE_CLASS);
        return StringUtils.hasText(delegateClassName) ? delegateClassName.trim() : null;
    }

    protected Filter getDelegateFilter() {
        return delegate;
    }

}
